home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / technote / tchntstc.sit / Technical Notes Stack 3.2.1 / Technical Notes Stack 3.2.1 / card_74032.txt < prev    next >
Encoding:
Text File  |  1990-01-08  |  16.9 KB  |  363 lines

  1. -- card: 74032 from stack: in.1
  2. -- bmap block id: 0
  3. -- flags: 0000
  4. -- background id: 2711
  5. -- name: 
  6.  
  7.  
  8. -- part contents for background part 9
  9. ----- text -----
  10. #240:    Using MPW for Non-Macintosh 68000 Systems
  11.  
  12. Written by:    Keith Rollin                                           June 1989
  13.  
  14. This Technical Note discusses using MPW 3.0 for creating software intended to run on 68000-based systems that do not implement the Macintosh run-time architecture.  These systems include NuBusΓäó cards, peripheral devices, and proprietary 68000 systems.
  15. _______________________________________________________________________________
  16.  
  17.  
  18. Introduction
  19.  
  20. Occasionally there is a need to create routines or programs for non-Macintosh systems.  Such situations can occur if you are writing a driver for a NuBus board, developing a peripheral that uses a 68xxx microprocessor, or perhaps targeting a proprietary 68xxx machine (Apple uses MPW for all of its ROM and NuBus development.)
  21.  
  22. For tasks such as this, MPW 3.0 can provide the solution.  This Note discusses the problems and issues that arise when doing using MPW 3.0 for this type of development, and it gives some hints and solutions.
  23.  
  24. To aid you in your efforts, there are several tools available on AppleLink in the Developer Services bulletin board (Developer Techical Support:Macintosh:Tools:Card Dev Tools:) and on Phil & DaveΓÇÖs Excellent CD.  These tools include utilities to generate checksum data and to prepare your program for downloading.
  25.  
  26. The following is a brief summary of problem areas you may encounter:
  27.  
  28.   ΓÇó  A5-Relative Globals
  29.   ΓÇó  Segmenting and the Jump Table
  30.   ΓÇó  ToolBox and OS Routines
  31.   ΓÇó  Setting up Your Run-Time Environment
  32.  
  33.  
  34. A5-Relative Globals
  35.  
  36. The Problem
  37.  
  38. In traditional machine environments, the compiler allocates a certain range of memory in which to store global variables.  This memory is established by the machineΓÇÖs memory architecture, and it can usually be referenced by using absolute addressing modes.
  39.  
  40. Because the Macintosh has a very dynamic run-time environment, programs cannot be written with specific memory locations in mind.  Programs are not given a fixed place in memory in which to store their data that will be the same between program invocations.  To solve this problem, all Macintosh programs are designed to store global variables in a 32K area pointed to by the 68000 register A5.  This could be a problem if your needs require you to reference or store your variables in specific memory locations.
  41.  
  42. The Solution
  43.  
  44. This problem can be solved if you are willing to use some macros.  A set of macros to do this could look something like the following:
  45.  
  46. #include    <sysEqu.h>
  47.  
  48. #define pShort           *(short *)
  49. #define pLong            *(long *)
  50. #define lmSFSaveDisk     pShort SFSaveDisk
  51. #define lmCurDirStore    pLong CurDirStore
  52.  
  53. main()
  54. {
  55.     short foo;
  56.     long bar;
  57.     
  58.     foo = lmSFSaveDisk;
  59.     lmCurDirStore = bar;
  60.  
  61. /* or */
  62.  
  63.     foo = pShort SFSaveDisk;
  64.     pLong CurDirStore = bar;
  65. }
  66.  
  67.  
  68. Segmenting and the Jump Table
  69.  
  70. The Problem
  71.  
  72. When the Macintosh was first developed, memory space was tight.  For this reason, a run-time architecture was designed that allowed programs to be divided into segments that could be dynamically loaded and unloaded.  Because of this, a program cannot rely on any specific memory locations into which it can be loaded, and hence it has to be freely relocatable.  This means that any intra-segment calls (i.e., calls from one routine to another within the same code segment) have to use the PC-relative addressing modes of the 68000.  Since these instructions use only signed 16-bit offsets, these branches are limited to a range of 32K bytes.  This, in turn, leads us to the historical 32K limit on 'CODE' resource segments.  While the restriction in the linker limiting
  73. 'CODE' resources to 32K has been lifted with MPW 3.0, it does not resolve the issues with long distance branching.
  74.  
  75. In order to be larger than 32K, a program should be divided into multiple 'CODE' resource segments.  Calls from a procedure in one segment to a procedure in another segment are called inter-segment calls.  These calls are performed through a jump table referenced with positive offsets from A5 (Refer to Inside Macintosh, Volume II-53, The Segment Loader, for more information on the jump table).  The problems that arise from this mechanism are that ROMable code does not get loaded into memory by a Segment Loader, and supporting an A5 jump table may not be desirable.
  76.  
  77.  
  78. The Solution
  79.  
  80. Programs compiled with Pascal or C currently always use the 16-bit PC-relative address mode when generating branch instructions.  There is no way to change that.  However, there are several ways you can get around it:
  81.  
  82.   ΓÇó  Implement your own A5 world
  83.   ΓÇó  Use islands for long jumps
  84.   ΓÇó  Use assembly language
  85.  
  86. 1)   Implement an A5 world in your device that mimics the MacintoshΓÇÖs as
  87.      closely as possible.  This is probably the easiest solution.  First,
  88.      you will be able to program in a normal Macintosh style and not have to
  89.      take into account considerations that are presented in solutions #2 and
  90.      #3.  It will also allow you to compile and link your program without
  91.      having to specify any special options.
  92.  
  93.      After this has been done, and you are ready to download your program to
  94.      its destination, you can run your program through a filter that:
  95.      a) determines the final locations of all of the 'CODE' resource segments
  96.      in the file, and b) creates a jump table with the addresses correctly
  97.      resolved.  In essence, this would be the same as a Macintosh program with
  98.      all of its segments loaded in memory at the same time.
  99.  
  100.      LetΓÇÖs take a look at an example.  Assume that you have developed a program
  101.      that is about 40K long, and you would like to have it loaded at location
  102.      $1000.  Because of its length, it is divided into two segments.  You have
  103.      one routine in 'CODE' = 1 that is referenced from 'CODE' = 2 and three
  104.      routines in 'CODE' = 2 that are referenced from 'CODE' = 1.  All of these
  105.      routines will generate jump table entries.  In addition, a jump table
  106.      entry is generated for the main entry point of your program, as per the
  107.      Segment Loader chapter of Inside Macintosh.  This gives us a total of five
  108.      jump table entries in our program.  The file created with MPW would look
  109.      something like the following:
  110.  
  111.      'CODE' = 1
  112.  
  113.      00000000:        main()
  114.      ...              ...
  115.      000038B4:        importantRoutine1()
  116.      ...              ...
  117.      000049F0         End of segment
  118.  
  119.      'CODE' = 2
  120.  
  121.      00000000:        importantRoutine2()
  122.      ...              ...
  123.      00003D0F:        importantRoutine3()
  124.      ...              ...
  125.      00004969:        importantRoutine4()
  126.      ...              ...
  127.      00005892         End of segment
  128.  
  129.      'CODE' = 0 (the jump table)
  130.  
  131.      00000000:
  132.      00000008:    $20 bytes of overhead
  133.      00000010:
  134.      00000018:
  135.      00000020:    00 00 3F 3C 00 01 A9 F0 ; dc.w $0000/MOVE.W #1,-(A7)/_LoadSeg
  136.      00000028:    38 B4 3F 3C 00 01 A9 F0
  137.      00000030:    00 00 3F 3C 00 02 A9 F0
  138.      00000038:    3D 0F 3F 3C 00 02 A9 F0
  139.      00000040:    49 69 3F 3C 00 02 A9 F0
  140.  
  141.      When we create our downloadable image, the routines that we are
  142.      interested in will end up at these locations:
  143.  
  144.      main()                 $0000 1000    ($1000 + $0000)
  145.      importantRoutine1()    $0000 48B4    ($1000 + $38B4)
  146.      importantRoutine2()    $0000 59F0    ($1000 + $49F0 + $0000)
  147.      importantRoutine3()    $0000 96FF    ($1000 + $49F0 + $3D0F)
  148.      importantRoutine4()    $0000 A359    ($1000 + $49F0 + $4969)
  149.  
  150.      Therefore, we should modify our Jump Table to look like this:
  151.  
  152.      'CODE' = 0 (the jump table)
  153.  
  154.      00000000:
  155.      00000008:    $20 bytes of overhead
  156.      00000010:
  157.      00000018:
  158.      00000020:    00 01 4E 59 00 00 10 00    ; dc.w $0001 / JMP $0000 1000
  159.      00000028:    00 01 4E 59 00 00 48 B4
  160.      00000030:    00 02 4E 59 00 00 59 F0
  161.      00000038:    00 02 4E 59 00 00 96 FF
  162.      00000040:    00 02 4E 59 00 00 A3 59
  163.  
  164. 2)   For some reason, it may be impossible or undesirable to segment your
  165.      code in Macintosh fashion.  You may be importing source code from
  166.      somewhere else, or you may not be able to utilize a jump table.  In cases
  167.      like this, where your program has to be compiled as one segment, you will
  168.      hit problems if it is a large program.  The Pascal and C compilers will
  169.      still limit you to branches smaller than 32K.  In the cases where you need
  170.      to execute long distance jumps, the only thing you can do is create
  171.      ΓÇ£islandsΓÇ¥ that allow you to make several short hops to your destination.
  172.      For instance, if it turns out that you are writing a C program which needs
  173.      to call a procedure that is 70K away, you will have to break up the branch
  174.      into three smaller ones as follows:
  175.  
  176.      main()
  177.  
  178.      [ ... some random code ... ]
  179.  
  180.      procedureNearTheBeginningOfMyProgram()
  181.      {
  182.          ...
  183.          Island1(); /*Calling importantButFarAwayRoutine() */
  184.          ...
  185.      }
  186.  
  187.      [ ... 20K of intervening code ... ]
  188.  
  189.      Island1()
  190.      { Island2(); }
  191.  
  192.      [ ... 30K of intervening code ... ]
  193.  
  194.      Island2()
  195.      { importantButFarAwayRoutine(); }
  196.  
  197.      [ ... 20K of intervening code ... ]
  198.  
  199.      importantButFarAwayRoutine()
  200.      {
  201.          ...
  202.      }
  203.  
  204. 3)   If programming little islands into your program is too gross for you to
  205.      comtemplate, then program using the 68xxx assembler, eschewing the
  206.      high-level compilers.  This will allow you to use the absolute addressing
  207.      mode directly, avoiding the fact that the compilers will not use them.
  208.      It will also allow you to store into and access fixed memory locations
  209.      more easily.  The following shows some ways of doing this:
  210.  
  211.      test    Main
  212.              import test5
  213.              import test6
  214.  
  215.              org $1000
  216.  
  217.              jsr test2            ; test of an intraprocedure call just
  218.              jmp test2            ; a few bytes away.
  219.  
  220.              jsr test3            ; test of an intraprocedure call a
  221.              jmp test3            ; significant number of bytes away.
  222.  
  223.      test2
  224.              jsr (test4).l        ; test of an intraprocedure call more
  225.              jmp (test4).l        ; than 32K away.
  226.  
  227.      ; The following instructions won't work on a 68000, but will on a
  228.      ; 68020 or better. They demonstrate a better alternative to the above
  229.      ; method, in that they generate PC-relative branching. In order to
  230.      ; use them, include "MACHINE MC68020" in your assembly source code.
  231.  
  232.      ;       bsr.l test4          ; test of an intraprocedure call more
  233.      ;       bra.l test4          ; than 32K away.
  234.  
  235.              lea (test4).l,A0     ; alternate test of a > 32K jump
  236.              jmp (A0)
  237.  
  238.              ds.b 17000           ; padding to force > 16K jump
  239.  
  240.      test3
  241.              jsr test5            ; test of an interprocedure call a
  242.              jmp test5            ; significant number of bytes away
  243.  
  244.              jsr (test6).l        ; test of an interprocedure call more
  245.              jmp (test6).l        ; than 32K away
  246.  
  247.              ds.b 17000           ; padding to force > 32K jump
  248.  
  249.      test4
  250.              rts
  251.              endp
  252.  
  253.      test5   proc
  254.              entry test6
  255.  
  256.              rts
  257.  
  258.              ds.b 17000           ; padding to force > 32K jump
  259.  
  260.      test6
  261.              rts
  262.  
  263.              endp
  264.              end
  265.  
  266.  
  267. Toolbox and OS Routines
  268.  
  269. The Problem
  270.  
  271. Because your program will be operating in a non-Macintosh environment, you will not be able to make any ToolBox or operating system calls.  This would not seem to be a problem until you consider that the library routines you are calling my be making such calls themselves.  For example, malloc() and most stdio calls fall into this category.
  272.  
  273. The Solution
  274.  
  275. DonΓÇÖt use our libraries.  Use your own.  Most of the MPW library routines are
  276. ΓÇ£clean,ΓÇ¥ but the low-level routines that they rely on use the Toolbox or OS.  Identifying those low-level routines that call the Macintosh operating system, determining all the high-level routines that depend upon them, and then programming around them is too difficult a task to undertake.  Even if it were done, you would still have to contend with routines that allocated global variables.  The best thing to do is avoid our libraries altogether and just write your own.
  277.  
  278.  
  279. Setting Up Your Run-Time Environment
  280.  
  281. The Problem
  282.  
  283. The Pascal and C compilers do some hidden work to initialize the run-time environment before the part of your application that you have written is actually executed.  It is possible that you may wish to take advantage of this setup or may need to duplicate it in order to get your program to execute.
  284.  
  285. The Solution
  286.  
  287. With Pascal, most of this initialization is automatically inserted into your main procedure.  There is very little you can do about it except to put all of your Pascal routines into separately compiled UNITs and write your entry point in C or assembly.
  288.  
  289. In the case of C, this initialization is performed by a routine in the file CRuntime.o called CMain().  The following is a description of what happens to your source code from the time the C compiler gets it to the time the code you have written is executed:
  290.  
  291.   ΓÇó  MPW C compiles all of the source files and creates object files for
  292.      the linker.  All functions are compiled in exactly the same way,
  293.      including main().
  294.   ΓÇó  These files are linked together.  If you do not link with the file
  295.      CRuntime.o, these routines will link together, but they will not
  296.      have an entry point; the linker will not have any routine explicitly
  297.      defined as the first one to be called, and it will default to setting
  298.      up the first routine that it finds as the entry point.
  299.   ΓÇó  If you do link with the file CRuntime.o, then you will be linking with
  300.      a routine called CMain().  This routine is marked as being an Entry
  301.      routine, and it will be the routine that is executed when you launch
  302.      the Macintosh program.
  303.      ΓÇó  CMain() performs the following steps:
  304.         1.  Call _RTInit (runtime init)
  305.         2.  Call setjmp()
  306.         3.  Check the result of setjmp().  If <> 0, go to 6.
  307.         4.  Call main()
  308.         5.  Call exit() with result from main().
  309.         6.  RTS
  310.         ΓÇó   This is what _RTInit does:
  311.             1.  Call _DataInit().
  312.             2.  Save the return address back to whomever ran this program.
  313.             3.  Check to see if launched by MPW.  If not, then setup argv
  314.                 and argc to indicate the name of the program with no parameters.
  315.             4.  If launched under MPW, initialize some things so that the
  316.                 run-time environment will integrate with MPW.  Calls the
  317.                 Memory Manager, so make sure that this part of the code is
  318.                 never executed.  This is not likely to happen, as _RTInit
  319.                 checks and validates several memory locations before it gets
  320.                 this far.
  321.             ΓÇó   This is what _DataInit() does:
  322.                 1.  Assume that A5 is valid, and that there is data appended
  323.                     to the end of DataInit that is used to initialize the
  324.                     globals.  This will be done by the linker automatically.
  325.                 2.  Determine the size of the globals and zero it out.
  326.                 3.  Read the data at the end of the procedure and use it to
  327.                     initialize the globals.  Normally, this process will
  328.                     attempt to use _BlockMove on sufficiently large blocks of
  329.                     data, and a small loop for small blocks of data.  A version
  330.                     of DataInit() that does not call _BlockMove is available
  331.                     from Macintosh Developer Technical Support.  However, this
  332.                     limits you to 64K of contiguous pre-initialized storage.
  333.      ΓÇó  This is what exit() does:
  334.         1.  Call any user installed exit procedures.
  335.         2.  If called from MPW, set the value of {Status}
  336.         3.  Determine if setjmp() was ever called.  If so then call longjmp()
  337.             with a value of 1.
  338.         4.  If setjmp() was never called, then return directly to the process
  339.             caller, as saved in step two of _RTInit.
  340.  
  341. While MPW was designed with creating Macintosh programs in mind, it can also be used to write software for non-Macintosh targets.  After resolving such issues as creating an appropriate run-time environment, making sure that Toolbox calls are not made, and being aware of the 32K limit for branches and jumps, you should be able to use the high-level Pascal and C compilers.  By using assembly language, you should even be able to avoid the problems that they pose.
  342.  
  343.  
  344. Further Reference:
  345. _______________________________________________________________________________
  346.   ΓÇó  Inside Macintosh, Volume II-53, The Segment Loader
  347.   ΓÇó  Technical Note #220, Segment Loader Limitations
  348.  
  349. NuBus is a trademark of Texas Instruments
  350.  
  351.  
  352. -- part contents for background part 2
  353. ----- text -----
  354. 240
  355.  
  356. -- part contents for background part 7
  357. ----- text -----
  358. Using MPW for Non-Macintosh 68000 Systems
  359.  
  360. -- part contents for background part 113
  361. ----- text -----
  362. Segment Loader
  363. Technical Note #220